#include "includes.h"
#include "perf_data.h"
#include "reader.h"
#include "stdlib.h"
#include <errno.h>
#include <stdio.h>
#include "init.h"
#include "io.h"

extern struct proc_info *active_proc;
const int CONTEXT_SWITCH_TIME = 1;

void handle_new_process() {
  struct proc_info *new_proc;

  /* If there is an active process, put it on the ready queue, and set to NULL */
  if (active_proc != NULL) {
    scheduler_add_to_queue(active_proc);
    active_proc = NULL;

    /* Advance clock now that active process is not running */
    advance_clock(CONTEXT_SWITCH_TIME);
  }

  /* Get the new process */
  new_proc = reader_get_next();

  if (new_proc == NULL) {
    perror("Reader passed null pointer to handle_new_process");
    exit(-1);
  }

  /* Add new process to the ready queue */
  scheduler_add_to_queue(new_proc);

}

void handle_resume_process() {
  /* Make sure no process is running */
  if (active_proc != NULL) {
    perror("Resuming process while active process was not null!!\n");
    exit(-1);
  } 
  /* Advance clock otherwise */
  else {
    advance_clock(CONTEXT_SWITCH_TIME);
  }

  /* Get the next active process */
  active_proc = scheduler_get_next();

  /* Active_proc will be null if there are no processes on the queue */
  if (active_proc == NULL) {
    perror("Null pointer passed from scheduler to resume process\n");
    exit(-1);
  }
}

void handle_kill_process() {

  if (active_proc == NULL) {
    perror("Unexpected null active process in handle_kill_process");
    exit(-1);
  }

  /* Set time of completion on ending process */
  active_proc->finish_time = clock_time;
  /* pass active process to stats machine */
  perf_handle_proc(active_proc);

  /* make active process null */
  active_proc = NULL;

  /* Context switch to kill process */
  advance_clock(CONTEXT_SWITCH_TIME);
}
  
void handle_start_io() {
  /* Make sure there IS an active process */
  if (active_proc == NULL) {
    perror("Unexpected null active process in handle_start_io");
    exit(-1);
  }
 
  /* Advance clocks while moving active to io */
  io_advance_clock(CONTEXT_SWITCH_TIME);
  scheduler_advance_clock(CONTEXT_SWITCH_TIME);
  clock_time += CONTEXT_SWITCH_TIME;
  /* Note how this section ^^ is different from the others 
     This is so that the running process does not get cpu time while
     in context switch. */
 
  /* Add active_proc to disk io queue */
  io_add_to_queue(active_proc);

  /* Make active proc null */
  active_proc = NULL;

  /* NOTE: We do not need to tell the scheduler we have removed process from
     execution since active_process will be null.  */
}

void handle_end_io()  {

  struct proc_info *io_proc;

  /* Reschedule active process */
  if (active_proc != NULL) {
    scheduler_add_to_queue(active_proc);
    active_proc = NULL;

    /* Advance clock for context switch */
    advance_clock(CONTEXT_SWITCH_TIME);
  }

  /* Get the completed process from io */
  io_proc = io_get_next();

  if (io_proc == NULL) {
    perror("Unexpected null process passed from io to handle_end_io.");
    exit(-1);
  }

  /* Reschedule if more cpu time */
  if (io_proc->time_remaining > 0) {
    scheduler_add_to_queue(io_proc);
  }
  /* No more cpu time, but ios remain */
  else if (io_proc->ios_remaining > 0) {
    io_add_to_queue(io_proc);
  }
  /* Otherwise process is finished */
  else {
    /* Set completion time */
    io_proc->finish_time = clock_time;
    /* Pass to performance data */
    perf_handle_proc(io_proc);
  }
}

void handle_schedule() {
  if (active_proc == NULL) {
    perror("Unexpected context switch when there was no active process");
    exit(-1);
  }

  /* Put job back onto ready queue */
  scheduler_add_to_queue(active_proc);

  /* Make active process null */
  active_proc = NULL;

  /* Active process out of execution, now advance clock */
  advance_clock(CONTEXT_SWITCH_TIME);
}
